package cn.uncode.rpc.core.protocol;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import cn.uncode.rpc.common.log.Logger;
import cn.uncode.rpc.common.log.LoggerFactory;
import cn.uncode.rpc.common.message.Messages;
import cn.uncode.rpc.core.Exporter;
import cn.uncode.rpc.core.Invoker;
import cn.uncode.rpc.core.Node;
import cn.uncode.rpc.core.Protocol;
import cn.uncode.rpc.core.URL;
import cn.uncode.rpc.exception.FrameworkException;
import cn.uncode.rpc.util.FrameworkUtil;


public abstract class AbstractProtocol implements Protocol {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractProtocol.class);
	
	protected ConcurrentHashMap<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();

	public Map<String, Exporter<?>> getExporterMap() {
        return Collections.unmodifiableMap(exporterMap);
    }
	
	@Override
	public <T> Exporter<T> export(Invoker<T> invoker, URL url) {
		if (url == null) {
            throw new FrameworkException(Messages.getString("RuntimeError.ExportError", this.getClass().getSimpleName(), "url is null"));
        }

        if (invoker == null) {
        	throw new FrameworkException(Messages.getString("RuntimeError.ExportError", this.getClass().getSimpleName(), "provider is null, url=" + url));
        }

        //injvm://192.168.1.61:8001/motan-demo-rpc/cn.uncode.rpc.api.DemoService/1.0
        String protocolKey = FrameworkUtil.getProtocolKey(url);

        synchronized (exporterMap) {
            Exporter<T> exporter = (Exporter<T>) exporterMap.get(protocolKey);

            if (exporter != null) {
            	throw new FrameworkException(Messages.getString("RuntimeError.ExportError", this.getClass().getSimpleName(), "service already exist, url=" + url));
            }

            exporter = createExporter(invoker, url);
            exporter.init();

            exporterMap.put(protocolKey, exporter);

            LOGGER.info(this.getClass().getSimpleName() + " export Success: url=" + url);

            return exporter;
        }
	}
	
	
	@Override
	public <T> Invoker<T> refer(Class<T> clz, URL url, URL serviceUrl) {
		if (url == null) {
			throw new FrameworkException(this.getClass().getSimpleName() + " refer Error: url is null");
		}

		if (clz == null) {
			throw new FrameworkException(
					this.getClass().getSimpleName() + " refer Error: class is null, url=" + url);
		}

		Invoker<T> referer = createInvoker(clz, url, serviceUrl);
		referer.init();

		LOGGER.info(this.getClass().getSimpleName() + " refer Success: url=" + url);

		return referer;
	}
	
	
    public <T> Invoker<T> refer(Class<T> clz, URL url) {
        return refer(clz, url, url);
    }
	
	protected abstract <T> Exporter<T> createExporter(Invoker<T> invoker, URL url);
	protected abstract <T> Invoker<T> createInvoker(Class<T> clz, URL url, URL serviceUrl);

	@Override
	public void destroy() {
		for (String key : exporterMap.keySet()) {
            Node node = exporterMap.remove(key);
            if (node != null) {
                try {
                    node.destroy();
                    LOGGER.info(this.getClass().getSimpleName() + " destroy node Success: " + node);
                } catch (Throwable t) {
                	LOGGER.error(this.getClass().getSimpleName() + " destroy Error", t);
                }
            }
        }

	}

}
